﻿namespace LizardChess
open System
open System.IO
open System.Runtime.Serialization.Formatters.Binary
open System.Runtime.Serialization
module Test =
    //STORAGE elements
    //set up paths
    let efold = 
        let ans = Path.Combine(Ref.basefold,"Test")
        Directory.CreateDirectory(ans)|>ignore
        ans
    let setfile = Path.Combine(efold,"Settings.txt")
    let setfilelin = Path.Combine(efold,"SettingsLin.txt")
    let resfile = Path.Combine(efold,"Results.txt")
    let resfilelin = Path.Combine(efold,"ResultsLin.txt")
    ///loadSettings - get stored setting random
    let loadSettings() = 
        if not (File.Exists setfile) then File.WriteAllLines(setfile,[|"30";"4"|])
        let lines = File.ReadAllLines(setfile)
        {num=(int)lines.[0];skip=(int)lines.[1]}
    ///loadSettingsLin - get stored setting linear
    let loadSettingsLin() = 
        if not (File.Exists setfilelin) then File.WriteAllLines(setfilelin,[|"10";"5"|])
        let lines = File.ReadAllLines(setfilelin)
        {numlin=(int)lines.[0];skiplin=(int)lines.[1]}
    ///saveSettings - save settings to file random
    let saveSettings(arr) = File.WriteAllLines(setfile,arr)
    ///saveSettingsLin - save settings to file linear
    let saveSettingsLin(arr) = File.WriteAllLines(setfilelin,arr)
    ///loadResults - get stored results
    let loadResults() = 
        if not (File.Exists resfile) then [||]
        else
            let lines = File.ReadAllLines(resfile) |> Array.map (fun l -> l.Split([|','|]))
            lines |> Array.map (fun l -> {vname=l.[0];visw=(l.[1]="T");dte=Convert.ToDateTime(l.[2]);res=(int)l.[3]})
    ///loadResultsLin - get stored results
    let loadResultsLin() = 
        if not (File.Exists resfilelin) then [||]
        else
            let lines = File.ReadAllLines(resfilelin) |> Array.map (fun l -> l.Split([|','|]))
            lines |> Array.map (fun l -> {vname=l.[0];visw=(l.[1]="T");dte=Convert.ToDateTime(l.[2]);res=(int)l.[3]})
    ///saveResults - save results to file
    let saveResults(arr) = File.WriteAllLines(resfile,arr)
    ///saveResultsLin - save results to file
    let saveResultsLin(arr) = File.WriteAllLines(resfilelin,arr)
    //saveRes - save results given both an array of results and a result
    let saveRes(resarr, res) =
        //need to first merge the res in
        let rec mrgres frl rrl r =
            if frl = [] then (r::rrl) |> List.rev |> List.toArray
            else
                let cr = frl.Head
                if cr.vname = r.vname && cr.visw = r.visw then (List.rev rrl) @ (r::frl.Tail) |> List.toArray
                else mrgres frl.Tail (cr::rrl) r
        let mrg = mrgres (resarr|>List.ofArray) [] res
        mrg |> Array.map (fun r -> r.vname + (if r.visw then ",T," else ",F,") + r.dte.ToLongDateString() + "," + r.res.ToString()) |> saveResults
    //saveRes - save results given both an array of results and a result
    let saveResLin(resarr, res) =
        //need to first merge the res in
        let rec mrgres frl rrl r =
            if frl = [] then (r::rrl) |> List.rev |> List.toArray
            else
                let cr = frl.Head
                if cr.vname = r.vname && cr.visw = r.visw then (List.rev rrl) @ (r::frl.Tail) |> List.toArray
                else mrgres frl.Tail (cr::rrl) r
        let mrg = mrgres (resarr|>List.ofArray) [] res
        mrg |> Array.map (fun r -> r.vname + (if r.visw then ",T," else ",F,") + r.dte.ToLongDateString() + "," + r.res.ToString()) |> saveResultsLin
    //createres - create a res given name, isw and res
    let createres(nm,isw,rs) = {vname=nm;visw=isw;dte=DateTime.Now;res=rs}
    //getallres - gets an array of an array of all results
    let getallres () =
        let reslst = loadResults() |> List.ofArray
        let wvars = Varn.wvars()
        let bvars = Varn.bvars()
        let rec getarr isw nm resl =
            if resl=[] then [|(if isw then "White" else "Black");nm;"N/A";"-"|]
            else
                let res = resl.Head
                if (res.vname=nm && res.visw=isw) then [|(if isw then "White" else "Black");nm;res.dte.ToLongDateString();res.res.ToString()|]
                else getarr isw nm resl.Tail

        let wl = wvars |> Array.map (fun nm -> getarr true nm reslst) |> List.ofArray
        let bl = bvars |> Array.map (fun nm -> getarr false nm reslst) |> List.ofArray
        (wl @ bl) |> List.toArray 
    //getallres - gets an array of an array of all results
    let getallreslin () =
        let reslst = loadResultsLin() |> List.ofArray
        let wvars = Varn.wvars()
        let bvars = Varn.bvars()
        let rec getarr isw nm resl =
            if resl=[] then [|(if isw then "White" else "Black");nm;"N/A";"-"|]
            else
                let res = resl.Head
                if (res.vname=nm && res.visw=isw) then [|(if isw then "White" else "Black");nm;res.dte.ToLongDateString();res.res.ToString()|]
                else getarr isw nm resl.Tail

        let wl = wvars |> Array.map (fun nm -> getarr true nm reslst) |> List.ofArray
        let bl = bvars |> Array.map (fun nm -> getarr false nm reslst) |> List.ofArray
        (wl @ bl) |> List.toArray 
    // GENERAL FUNCTIONS
    let GetPosn tst = Posn.DoMoves(tst.mvl,Posn.st())
    let rec ListIncl (tst:test) tstlst =
        if tstlst = [] then false
        elif GetPosn(tstlst.Head).Sqs = GetPosn(tst).Sqs then true
        else ListIncl tst tstlst.Tail 
    /// ListAdd - only adds if not present
    let ListAdd tst tstlst = if ListIncl tst tstlst then tstlst else tst::tstlst
    //get a ful array of test positions from a line
    let rec fromLine front rear tests nm =
        if rear = [] then tests
        elif rear.Tail=[] then ListAdd {mvl=front;mv=rear.Head;vnname=nm} tests
        else
            let mv1 = rear.Head
            let mv2 = rear.Tail.Head
            fromLine (front@[mv1;mv2]) rear.Tail.Tail (ListAdd {mvl=front;mv=mv1;vnname=nm} tests) nm
    //get a full array of test positions from a variation fro random tests
    let fromVarn (vn:varn) =
        let tstlst = if vn.isw then vn.brchs |> List.map (fun b -> fromLine [] (List.rev b.Mhst) [] (vn.name)) else vn.brchs |> List.map (fun b -> fromLine [(List.rev b.Mhst).Head] (List.rev b.Mhst).Tail [] (vn.name))
        tstlst |> List.concat |> List.toArray
    // fromName - generate set of test positions given name of variation and whether white
    let fromName vnname isw =
        let vnnames =
            if vnname = "<All>" then 
                if isw then Varn.wvars() else Varn.bvars()
            else
                [|vnname|]
        let vns = vnnames |> Array.map (fun nm -> Varn.load(nm,isw))
        let alltests = vns |> Array.map fromVarn |> Array.concat 
        //load settings
        let sets = loadSettings()
        //filter to remove early positions
        let alltestsfilt1 = alltests |> Array.filter (fun t -> t.mvl.Length > sets.skip * 2)
        //reset if no tests left
        let alltestsfilt = if alltestsfilt1.Length=0 then alltests else alltestsfilt1
        //select random subset
        let alltestgenl = new System.Collections.Generic.List<test>(alltestsfilt)
        let num = if alltestsfilt.Length<sets.num then alltestsfilt.Length else sets.num
        // gets random set
        let ans = Array.zeroCreate num
        let rnd = System.Random()
        for i = 0 to num-1 do
            let c = rnd.Next(alltestgenl.Count-1)
            ans.[i] <- alltestgenl.[c]
            alltestgenl.RemoveAt(c)
        ans
    // fromNameLin - generate set of test positions given name of variation and whether white
    let fromNameLin vnname isw =
        // get settings
        let sets = loadSettingsLin()
        // get all lines
        let vn = Varn.load(vnname,isw)
        let tstlst = if vn.isw then vn.brchs |> List.map (fun b -> fromLine [] (List.rev b.Mhst) [] (vn.name)) else vn.brchs |> List.map (fun b -> fromLine [(List.rev b.Mhst).Head] (List.rev b.Mhst).Tail [] (vn.name))
        //select random subset
        let alltestgenl = new System.Collections.Generic.List<test list>(tstlst)
        let num = if tstlst.Length<sets.numlin then tstlst.Length else sets.numlin
        // gets random set
        let tstlstsub = Array.zeroCreate num
        let rnd = System.Random()
        for i = 0 to num-1 do
            let c = rnd.Next(alltestgenl.Count-1)
            tstlstsub.[i] <- alltestgenl.[c]
            alltestgenl.RemoveAt(c)
        // reverse move list and concatenate
        let tstarr = tstlstsub |> Array.map (fun l -> List.rev l) |> List.concat |> List.toArray
        //filter to remove early positions
        let tstarrfilt1 = tstarr |> Array.filter (fun t -> t.mvl.Length > sets.skiplin * 2)
        //reset if no tests left
        let tstarrfilt = if tstarrfilt1.Length=0 then tstarr else tstarrfilt1
        tstarrfilt
